#ifndef __EPSILON_3D__RENDERCONTEXT_H__
#define __EPSILON_3D__RENDERCONTEXT_H__

#include "driver.h"

class eWindow;
class eFrameTimer;

class eImage;

class e3dTexture;
class e3dVertexCache;
class e3dIndexCache;

class e3dRenderContext;
class e3dPixelBuffer;

/**
 * Interface for Render Context APIs
 *
**/
class e3dRenderContextAPI {
public:
  e3dRenderContextAPI (void) {
    // TODO
    _driver = NULL;
    _base   = NULL;
  }

  virtual ~e3dRenderContextAPI (void) {
    destroy ();
  }

  virtual bool create  (eWindow* win, e3dRenderContext* base) = 0;
  virtual void destroy (void);

  virtual void begin_frame (void) = 0;
  virtual void end_frame   (void) = 0;

  virtual void resize (int width, int height) = 0;

  virtual
    e3dPixelBuffer*
               get_pixels (int x, int y, int num) = 0;

  // TODO: Use the backend in _driver instead of subclassing this.
  virtual e3dTexture*     create_texture_2d   (eImage* img, bool mipmap) = 0;
  virtual e3dVertexCache* create_vertex_cache (void) = 0;
  virtual e3dIndexCache*  create_index_cache  (void) = 0;

private:
  e3dDriver*        _driver;
  e3dRenderContext* _base;
};

/**
 * Generic Render Context
 *
 *   @ Stores render states, textures, shaders and vertices
 *
**/
class e3dRenderContext : public e3dRenderState {
public:
           e3dRenderContext (void) : _imp   (NULL),
                                     _timer (NULL) { };
  virtual ~e3dRenderContext (void)
  {
    Destroy ();
  }

  bool  Create     (eWindow* win);
  void  Destroy    (void);

  void  BeginFrame (void);
  void  EndFrame   (void);

  e3dPixelBuffer*
        GetPixels (int x = 0, int y = 0, int num = -1);

  void  Resize (int width, int height);

  float GetFPS     (void);


  // Resource management
  e3dTexture*     CreateTexture2D   (eImage* img, bool mipmap = true);
  e3dVertexCache* CreateVertexCache (void);
  e3dIndexCache*  CreateIndexCache  (void);


protected:
  e3dRenderContextAPI* _imp;
  eFrameTimer*         _timer;
};

#ifdef WIN32
#include <windows.h>

class e3dRenderContextWin32
{
public:
           e3dRenderContextWin32 (void) : hWnd (NULL) { };
  virtual ~e3dRenderContextWin32 (void) { };

protected:
  HWND hWnd;
};
#endif

#ifdef __APPLE__
#include <Carbon/Carbon.h>
#include "AGL/agl.h"
#endif

namespace e3dOpenGL
{
  class RenderContextImp
  {
  public:
    virtual bool create  (eWindow* win, e3dRenderContext* base) = 0;
    virtual void destroy (void)                                 = 0;

    virtual void begin_frame (void) = 0;
    virtual void end_frame   (void) = 0;

    virtual void resize (int width, int height) = 0;

    virtual
      e3dPixelBuffer*
                 get_pixels (int x, int y, int num) = 0;
  };

  class RenderContext : public e3dRenderContextAPI
  {
  public:
             RenderContext (void) : _imp  (NULL),
                                    _base (NULL) { };
    virtual ~RenderContext (void) {
      destroy ();
    };

    bool create  (eWindow* win, e3dRenderContext* base);
    void destroy (void);

    void begin_frame (void);
    void end_frame   (void);

    void resize (int width, int height);

    e3dPixelBuffer*
         get_pixels (int x, int y, int num);

    // Resource management (NOTE: Does not depend on the imp.)
    e3dTexture*
         create_texture_2d   (eImage* img, bool mipmap);

    e3dVertexCache*
         create_vertex_cache (void);

    e3dIndexCache*
         create_index_cache  (void);

  private:
    RenderContextImp* _imp;
    e3dRenderContext* _base;
  };

#ifdef WIN32
  namespace Win32 /* e3dOpenGL::Win32 */
  {
    class RenderContext : public e3dRenderContextWin32,
                          public RenderContextImp
    {
    public:
      RenderContext (void) : _base       (NULL),
                             hDC         (NULL),
                             hRC         (NULL),
                             PixelFormat (0) { };

      bool create  (eWindow* win, e3dRenderContext* base);
      void destroy (void);

      void begin_frame (void);
      void end_frame   (void);

      void resize (int width, int height);

      e3dPixelBuffer*
           get_pixels (int x, int y, int num);

    protected:
      HDC   hDC;
      HGLRC hRC;
      int   PixelFormat;

    private:
      e3dRenderContext* _base;

      static HGLRC hrc_base;
    };
  };
#endif

  namespace X11 /* e3dOpenGL::X11 */
  {
    class RenderContext : public RenderContextImp
    {
    public:
      RenderContext (void) : _base (NULL) { };

    //  bool create (e3dWindow* win, e3dRenderContext* base);

    private:
      e3dRenderContext* _base;
    };
  };

#ifdef __APPLE__
  namespace Quartz /* e3dOpenGL::Quartz */
  {
    class RenderContext : public RenderContextImp
    {
    public:
      RenderContext (void) : _base (NULL) { };

      bool create  (eWindow* win, e3dRenderContext* base);
      void destroy (void);

      void begin_frame (void);
      void end_frame   (void);

      void resize (int width, int height);

      e3dPixelBuffer*
           get_pixels (int x, int y, int num);

    protected:
      WindowRef      window;
      AGLContext     context;
      AGLPixelFormat pix_fmt;

    private:
      e3dRenderContext* _base;
    };
  };
#endif
};


#ifdef HAS_D3D
#include <d3d9.h>
#include <d3dx9.h>

#include "../debug.h"

namespace e3dDirect3D
{
  class RenderContext : public e3dRenderContextWin32,
                        public e3dRenderContextAPI
  {
  public:
    RenderContext (void) //: /*caps  (NULL),*/
                           /*dev   (NULL)*/
                           /*d3dpp (NULL) */ { };

    bool create  (eWindow* window, e3dRenderContext* base);
    void destroy (void);

    void begin_frame (void);
    void end_frame   (void);

    e3dPixelBuffer*
         get_pixels  (int x, int y, int num);

    void resize (int width, int height);

    // Resource management
    e3dTexture*
         create_texture_2d   (eImage* img, bool mipmap);

    e3dVertexCache*
         create_vertex_cache (void);

    e3dIndexCache*
         create_index_cache  (void);

  private:
    e3dRenderContext* _base;

    D3DCAPS9              caps;
    D3DPRESENT_PARAMETERS d3dpp;

    LPDIRECT3DSWAPCHAIN9  swapchain; ///< The equiv. of a shared context in GL.
  };


  class Device
  {
  public:
    Device (void) {
      dev    = NULL;
      width  = 0;
      height = 0;
      format = D3DFMT_UNKNOWN;
    }

    virtual ~Device (void) {
      if (dev) {
        dev->Release ();
      }
    }

    operator LPDIRECT3DDEVICE9 (void) {
      return dev;
    }

  public:
    LPDIRECT3DDEVICE9 dev;

    int               width;
    int               height;
    D3DFORMAT         format;
  };

  

  extern LPDIRECT3D9  d3d;
  extern Device*      dev;


  inline
    bool IsDevicePresent (void) {
      return (dev != NULL);
    }

  inline
    LPDIRECT3DDEVICE9 GetDev (void) {
      Assert (dev != NULL, _T ("No Direct3D Device Present!"));

      if (dev != NULL)
        return dev->dev;

      return NULL;
    }
};
#endif


#endif /* __EPSILON_3D__RENDERCONTEXT_H__ */
